using System;
using BestHTTP.WebSocket;
using UnityEngine;

namespace Mars.Client
{
	class StateObject
	{
		public const int BufferSize = 1024;
		internal byte[] buffer = new byte[BufferSize];
	}
	
	public class Transporter
	{
		public const int HeadLength = 4;
		
		private WebSocket socket;
		private Action<byte[]> messageProcesser;

		private TransportState transportState;

		private bool onSending = false;
		private bool onReceiving = false;

		private byte[] headBuffer = new byte[4];
		private byte[] buffer;
		private int bufferOffset = 0;
		private int pkgLength = 0;
		internal Action onDisconnect = null;
		
		public Transporter ( WebSocket socket, Action<byte[]> processer ){
			this.socket = socket;
			this.messageProcesser = processer;
			transportState = TransportState.readHead;
		}
		
		public void start(){
			this.socket.OnBinary += OnBinaryMessageReceived;
			this.receive ();
		}
		
		public void send(byte[] buffer){
			if(this.transportState != TransportState.closed){ 
				this.socket.Send( buffer );
				this.onSending = false; //这里要hack一下besthttp 加一个异步事件 因此下面那个 sendcallback 暂时用不了了
			}
		}
		
//		private void sendCallback(IAsyncResult asyncSend){
//			if(this.transportState == TransportState.closed) return;
////			socket.EndSend (asyncSend);
//			this.onSending = false;
//		}

		void OnBinaryMessageReceived(WebSocket webSocket, byte[] message) {
					
			Debug.Log(" enter Binary Message received ");
			
			if(this.transportState == TransportState.closed) return;
		
			WebSocket socket 	= this.socket;
			int length 			= message.Length;
			
			Debug.Log("Binary Message received from server. Length: " + length);

			this.onReceiving = false;
			
			if (length > 0) {
				processBytes( message , 0, length );
				//Receive next message
				//not need receive next message
			} else{
				if(this.onDisconnect != null) this.onDisconnect();
			}
			
			this.onReceiving = false;
		}
		
		internal void close(){
			this.transportState = TransportState.closed;
		}
		
		public void receive(){
			Debug.Log( String.Format ("receive state : {0}, {1}", this.transportState, this.socket.IsOpen) );
			if (!socket.IsOpen) {
				this.socket.Open();			
			}
			this.onReceiving = true;
		}

		internal void processBytes(byte[] bytes, int offset, int limit){

			Debug.Log( " staret process bytes " );

			if (this.transportState == TransportState.readHead) {
				readHead (bytes, offset, limit);
			} else if (this.transportState == TransportState.readBody) {
				readBody (bytes, offset, limit);
			}
		}
		
		private bool readHead(byte[] bytes, int offset, int limit){
			int length = limit - offset;
			int headNum = HeadLength - bufferOffset;
			
			if(length >= headNum){
				//Write head buffer
				writeBytes(bytes, offset, headNum, bufferOffset, headBuffer);
				//Get package length
				pkgLength = (headBuffer [1] << 16) + (headBuffer [2] << 8) + headBuffer [3];
				
				//Init message buffer
				buffer = new byte[HeadLength + pkgLength];
				writeBytes (headBuffer, 0, HeadLength, buffer);
				offset += headNum;
				bufferOffset = HeadLength;
				this.transportState = TransportState.readBody;
				
				if(offset <= limit) processBytes(bytes, offset, limit);
				return true;
			}else{
				writeBytes(bytes, offset, length, bufferOffset, headBuffer);
				bufferOffset += length;
				return false;
			}
		}
		
		private void readBody(byte[] bytes, int offset, int limit){
			int length = pkgLength + HeadLength - bufferOffset;
			if ((offset + length) <= limit) {
				writeBytes (bytes, offset, length, bufferOffset, buffer);
				offset += length;
				
				//Invoke the protocol api to handle the message
				this.messageProcesser.Invoke(buffer);
				this.bufferOffset = 0;
				this.pkgLength = 0;
				
				if(this.transportState != TransportState.closed) this.transportState = TransportState.readHead;
				if(offset< limit) processBytes(bytes, offset, limit); 
			} else {
				writeBytes (bytes, offset, limit - offset, bufferOffset, buffer);
				bufferOffset += limit - offset;
				this.transportState = TransportState.readBody;
			}			
		}
		
		private void writeBytes(byte [] source, int start, int length, byte [] target){
			writeBytes(source, start, length, 0, target);
		}
		
		private void writeBytes(byte [] source, int start, int length, int offset, byte [] target){
			for(int i = 0; i < length; i++){
				target[offset + i] = source[start + i];
			}
		}
		
		private void print(byte[] bytes, int offset, int length){
			for(int i = offset; i < length; i++)
				Debug.Log(Convert.ToString(bytes[i], 16) + " ");
			Debug.Log("\r");
		}
	}
}